package com.whiteape.book.core.manager;

import com.kunfei.bookshelf.bean.BaseChapterBean;
import com.kunfei.bookshelf.bean.BookChapterBean;
import com.kunfei.bookshelf.bean.BookInfoBean;
import com.kunfei.bookshelf.bean.BookShelfBean;
import com.kunfei.bookshelf.bean.BookSourceBean;
import com.kunfei.bookshelf.bean.SearchBookBean;
import com.kunfei.bookshelf.model.WebBookModel;
import com.whiteape.book.common.WaConstants;
import com.whiteape.book.model.bo.CategoryDetailBo;
import com.whiteape.book.model.bo.ChapterContentBo;
import com.whiteape.book.model.bo.SearchResultBo;
import com.whiteape.book.model.dto.CollectionMediaBean;
import com.whiteape.book.model.dto.MediaChapterBean;
import com.whiteape.book.model.dto.SiteSourceBean;
import com.whiteape.book.model.dto.SiteSourceCategoryBean;
import com.whiteape.book.model.type.MediaType;
import com.whiteape.book.model.type.SourceFromType;
import com.whiteape.book.utils.GsonUtils;
import com.whiteape.book.utils.MD5Utils;
import com.whiteape.book.utils.RxUtils;
import com.whiteape.book.utils.StringUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

import io.legado.app.data.entities.Book;
import io.legado.app.data.entities.BookChapter;
import io.legado.app.data.entities.BookSource;
import io.legado.app.data.entities.SearchBook;
import io.legado.app.help.coroutine.Coroutine;
import io.legado.app.model.webBook.CallBack;
import io.legado.app.model.webBook.WebBook;
import io.reactivex.Observable;
import io.reactivex.Scheduler;
import io.reactivex.Single;
import io.reactivex.SingleOnSubscribe;
import io.reactivex.schedulers.Schedulers;
import kotlin.Pair;
import kotlin.coroutines.CoroutineContext;
import kotlin.coroutines.EmptyCoroutineContext;
//import kotlinx.coroutines.Dispatchers;
import kotlinx.coroutines.GlobalScope;
//import nl.siegmann.epublib.domain.Book;

/**
 * 外部引擎管理器
 * <p>
 * 来自于类似阅读2.0、阅读3.0、搜书大师、轻阅等的源
 */
public class ExtraEngineManager {
    private ExtraEngineManager() {
    }

    private Executor executor = Executors.newFixedThreadPool(WaConstants.FIX_THREAD_NUM);
    private Scheduler scheduler = Schedulers.from(executor);

    private static volatile ExtraEngineManager instance;

    public static ExtraEngineManager getInstance() {
        if (instance == null) {
            synchronized (ExtraEngineManager.class) {
                if (instance == null) {
                    instance = new ExtraEngineManager();
                }
            }
        }
        return instance;
    }

    public Single<List<CategoryDetailBo>> getSourceCategory(SiteSourceBean siteSourceBean, SiteSourceCategoryBean categoryBean, String url, int page, String tag){
        Integer sourceFrom = siteSourceBean.getSourceFrom();
        if(sourceFrom == null){
            return Single.create(emitter -> emitter.onError(new Throwable("source from is null")));
        }
        if(sourceFrom == SourceFromType.SOURCE_FROM_YUEDU2){
            BookSourceBean bookSourceBean = GsonUtils.string2Obj(siteSourceBean.getExtraSource(), BookSourceBean.class);
            return Single.create(emitter -> {
                List<SearchBookBean> bookBeanList = WebBookModel.getInstance().findBook(url, page, tag, bookSourceBean).blockingSingle();
                List<CategoryDetailBo> categoryDetailBoList = new ArrayList<>();
                if(bookBeanList != null && bookBeanList.size() > 0){
                    for(SearchBookBean bookBean : bookBeanList){
                        CategoryDetailBo detailBo = new CategoryDetailBo();
                        detailBo.setAuthor(bookBean.getAuthor());
                        detailBo.setCoverUrl(bookBean.getCoverUrl());
                        detailBo.setDesc(bookBean.getIntroduce());
                        detailBo.setDetailUrl(bookBean.getNoteUrl());
                        detailBo.setTitle(bookBean.getName());
                        detailBo.setLastChapter(bookBean.getLastChapter());
                        detailBo.setSiteSourceBean(siteSourceBean);
                        detailBo.setMediaType(MediaType.MEDIA_TYPE_NOVEL);
                        detailBo.setCategoryBean(categoryBean);
                        detailBo.setSiteSourceBean(siteSourceBean);
                        categoryDetailBoList.add(detailBo);
                    }
                }
                emitter.onSuccess(categoryDetailBoList);
            });


        }else if(sourceFrom == SourceFromType.SOURCE_FROM_YUEDU3){
            BookSource bookSourceBean = GsonUtils.string2Obj(siteSourceBean.getExtraSource(), BookSource.class);
            return new WebBook(bookSourceBean).exploreBookSingle(url, page).map(searchBooks -> {
                List<CategoryDetailBo> categoryDetailBoList = new ArrayList<>();
                if(searchBooks!= null && searchBooks.size() > 0){
                    for(SearchBook bookBean : searchBooks){
                        CategoryDetailBo detailBo = new CategoryDetailBo();
                        detailBo.setAuthor(bookBean.getAuthor());
                        detailBo.setCoverUrl(bookBean.getCoverUrl());
                        detailBo.setDesc(bookBean.getIntro());
                        detailBo.setDetailUrl(bookBean.getBookUrl());
                        detailBo.setTitle(bookBean.getName());
                        detailBo.setLastChapter(bookBean.getLatestChapterTitle());
                        detailBo.setSiteSourceBean(siteSourceBean);
                        detailBo.setMediaType(siteSourceBean.getSiteType());
                        detailBo.setCategoryBean(categoryBean);
                        detailBo.setSiteSourceBean(siteSourceBean);
                        categoryDetailBoList.add(detailBo);
                    }
                }
                return categoryDetailBoList;
            });
        }else{
            return Single.create(emitter -> emitter.onError(new Throwable("wrong source type from")));
        }
    }
    public Observable<List<SearchResultBo>> getSearchInfo(String searchKey, SiteSourceBean siteSourceBean) {

            if (siteSourceBean == null || org.apache.commons.lang3.StringUtils.isEmpty(searchKey)) {
                return Observable.create(emitter -> emitter.onError(new Throwable("getSearchInfo:siteSourceBean is null or searchKey is empty")));
            }
            if (siteSourceBean.getSourceFrom()!= null && siteSourceBean.getSourceFrom() == SourceFromType.SOURCE_FROM_YUEDU2) {
                String extraSourceBeanStr = siteSourceBean.getExtraSource();
                BookSourceBean bookSourceBean = GsonUtils.string2Obj(extraSourceBeanStr, BookSourceBean.class);
                Observable<List<SearchResultBo>> observable = WebBookModel.getInstance().searchBook(searchKey, 1, bookSourceBean.getBookSourceUrl(), bookSourceBean).map(searchBookBeans -> {

                    List<SearchResultBo> ret = new ArrayList<>();
                    for (SearchBookBean bookBean : searchBookBeans) {
                        SearchResultBo searchResultBo = new SearchResultBo();
                        searchResultBo.setAuthor(bookBean.getAuthor());
                        searchResultBo.setCategory(bookBean.getKind());
                        searchResultBo.setCoverUrl(bookBean.getCoverUrl());
                        searchResultBo.setDesc(bookBean.getIntroduce());
                        searchResultBo.setDetailUrl(bookBean.getNoteUrl());
                        searchResultBo.setLastChapter(bookBean.getLastChapter());
                        searchResultBo.setTitle(bookBean.getName());
                        searchResultBo.setMediaType(siteSourceBean.getSiteType());

                        searchResultBo.setSiteSourceBean(siteSourceBean);
                        ret.add(searchResultBo);
                    }
                    return ret;
                });
                return observable.compose(RxUtils::toSimpleSingle);
            } else if (siteSourceBean.getSourceFrom()!= null && siteSourceBean.getSourceFrom() == SourceFromType.SOURCE_FROM_YUEDU3) {
                String extraSourceBeanStr = siteSourceBean.getExtraSource();
                BookSource bookSourceBean = GsonUtils.string2Obj(extraSourceBeanStr, BookSource.class);
               Observable<List<SearchResultBo>> observable = new WebBook(bookSourceBean).searchBookSingle(searchKey, 1).map(searchBooks -> {
                   List<SearchResultBo> ret = new ArrayList<>();
                   for (SearchBook bookBean : searchBooks) {
                       SearchResultBo searchResultBo = new SearchResultBo();
                       searchResultBo.setAuthor(bookBean.getAuthor());
                       searchResultBo.setCategory(bookBean.getKind());
                       searchResultBo.setCoverUrl(bookBean.getCoverUrl());
                       searchResultBo.setDesc(bookBean.getIntro());
                       searchResultBo.setDetailUrl(bookBean.getBookUrl());
                       searchResultBo.setLastChapter(bookBean.getLatestChapterTitle());
                       searchResultBo.setTitle(bookBean.getName());
                       searchResultBo.setMediaType(siteSourceBean.getSiteType());

                       searchResultBo.setSiteSourceBean(siteSourceBean);
                       ret.add(searchResultBo);
                   }
                   return ret;
               }).toObservable();

                return observable.compose(RxUtils::toSimpleSingle);
            }
        return Observable.create(emitter -> emitter.onError(new Throwable("未知异常")));
    }

    public Observable<List<SearchResultBo>> getSearchInfo(String searchKey, List<SiteSourceBean> siteSourceBeanList) {
        List<Observable<List<SearchResultBo>>> searchObservableList = new ArrayList<>(siteSourceBeanList.size());
        for (SiteSourceBean siteSourceBean : siteSourceBeanList) {

            if (siteSourceBean == null || org.apache.commons.lang3.StringUtils.isEmpty(searchKey)) {
                return Observable.create(emitter -> emitter.onError(new Throwable("getSearchInfo:siteSourceBean is null or searchKey is empty")));
            }
            if (siteSourceBean.getSourceFrom()!= null && siteSourceBean.getSourceFrom() == SourceFromType.SOURCE_FROM_YUEDU2) {
                String extraSourceBeanStr = siteSourceBean.getExtraSource();
                BookSourceBean bookSourceBean = GsonUtils.string2Obj(extraSourceBeanStr, BookSourceBean.class);
                Observable<List<SearchResultBo>> observable = WebBookModel.getInstance().searchBook(searchKey, 1, bookSourceBean.getBookSourceUrl(), bookSourceBean).map(searchBookBeans -> {

                    List<SearchResultBo> ret = new ArrayList<>();
                    for (SearchBookBean bookBean : searchBookBeans) {
                        SearchResultBo searchResultBo = new SearchResultBo();
                        searchResultBo.setAuthor(bookBean.getAuthor());
                        searchResultBo.setCategory(bookBean.getKind());
                        searchResultBo.setCoverUrl(bookBean.getCoverUrl());
                        searchResultBo.setDesc(bookBean.getIntroduce());
                        searchResultBo.setDetailUrl(bookBean.getNoteUrl());
                        searchResultBo.setLastChapter(bookBean.getLastChapter());
                        searchResultBo.setTitle(bookBean.getName());
                        searchResultBo.setMediaType(MediaType.MEDIA_TYPE_NOVEL);

                        searchResultBo.setSiteSourceBean(siteSourceBean);
                        ret.add(searchResultBo);
                    }
                    return ret;
                });
                searchObservableList.add(observable);
            }
        }
        return Observable.mergeDelayError(searchObservableList);
    }

    public Single<CollectionMediaBean> getMediaDetail(CollectionMediaBean collectionMediaBean, SiteSourceBean siteSourceBean) {
        if (siteSourceBean.getSourceFrom()!= null && siteSourceBean.getSourceFrom() == SourceFromType.SOURCE_FROM_WHITEAPE) {
            return Single.create(emitter -> emitter.onError(new Throwable("非外部源")));
        }
        BookShelfBean bookShelfBean = convertToBookshelfBean(collectionMediaBean);
        bookShelfBean.setTag(siteSourceBean.getSiteHostUrl());
        BookSourceBean bookSourceBean = GsonUtils.string2Obj(siteSourceBean.getExtraSource(), BookSourceBean.class);
        if (siteSourceBean.getSourceFrom()!= null && siteSourceBean.getSourceFrom() == SourceFromType.SOURCE_FROM_YUEDU2) {

            return WebBookModel.getInstance().getBookInfo(bookShelfBean, bookSourceBean).firstOrError().map(bookShelfBean1 -> {
                CollectionMediaBean collectionMediaBean1 = convertToCollectionMediaBean(bookShelfBean1);
                collectionMediaBean1.setSiteSourceBean(siteSourceBean);
                return collectionMediaBean1;
            });
        }else if (siteSourceBean.getSourceFrom() != null && siteSourceBean.getSourceFrom() == SourceFromType.SOURCE_FROM_YUEDU3){
            BookSource bookSource = GsonUtils.string2Obj(siteSourceBean.getExtraSource(), BookSource.class);
            Book book = convertToYuedu3Book(collectionMediaBean);
            return new WebBook(bookSource).getBookInfoSingle(book).map(book1 -> {
                CollectionMediaBean collectionMediaBean1 = convertYuedu3BookToCollectionMediaBean(siteSourceBean, book1);
                collectionMediaBean1.setSiteSourceBean(siteSourceBean);
                return collectionMediaBean1;
            });
        }
        return Single.create(emitter -> emitter.onError(new Throwable("未适配的外部源")));
    }

    public Single<List<MediaChapterBean>> getCatalog(CollectionMediaBean collectionMediaBean, SiteSourceBean siteSourceBean) {
        if (siteSourceBean.getSourceFrom()!= null && siteSourceBean.getSourceFrom() == SourceFromType.SOURCE_FROM_WHITEAPE) {
            return Single.create(emitter -> emitter.onError(new Throwable("非外部源")));
        }
        BookShelfBean bookShelfBean = convertToBookshelfBean(collectionMediaBean);
        BookSourceBean bookSourceBean = GsonUtils.string2Obj(siteSourceBean.getExtraSource(), BookSourceBean.class);
        if (siteSourceBean.getSourceFrom()!= null && siteSourceBean.getSourceFrom() == SourceFromType.SOURCE_FROM_YUEDU2) {
            return WebBookModel.getInstance().getChapterList(bookShelfBean, bookSourceBean).firstOrError().map(bookChapterBeans -> {
                List<MediaChapterBean> ret = new ArrayList<>();
                for (BookChapterBean bookChapterBean : bookChapterBeans) {
                    MediaChapterBean mediaChapterBean = new MediaChapterBean();
                    String link = bookChapterBean.getDurChapterUrl();
                    mediaChapterBean.setId(MD5Utils.strToMd5By16(link));
                    mediaChapterBean.setLink(link);
                    mediaChapterBean.setName(bookChapterBean.getDurChapterName());
                    ret.add(mediaChapterBean);
                }
                return ret;
            });
        }else if(siteSourceBean.getSourceFrom() != null && siteSourceBean.getSourceFrom() == SourceFromType.SOURCE_FROM_YUEDU3){
            BookSource bookSource = GsonUtils.string2Obj(siteSourceBean.getExtraSource(), BookSource.class);
            Book book = convertToYuedu3Book(collectionMediaBean);
            return new WebBook(bookSource).getChapterListSingle(book).map(bookChapters -> {
                List<MediaChapterBean> ret = new ArrayList<>();
                for (BookChapter bookChapter : bookChapters){
                    MediaChapterBean mediaChapterBean = new MediaChapterBean();
//                    String link = bookChapter.getAbsoluteURL();
//                    String link = bookChapter.getBookUrl();
                    String link = bookChapter.getUrl();
                    mediaChapterBean.setId(MD5Utils.strToMd5By16(link));
                    mediaChapterBean.setLink(link);
                    mediaChapterBean.setName(bookChapter.getTitle());
                    ret.add(mediaChapterBean);
                }
                return ret;
            });
        }
        return Single.create(emitter -> emitter.onError(new Throwable("未适配的外部源")));
    }

    //yuedu3 通过novel类型获得的文本结果，用comic方式处理出来，内容一开始是 <img src='xxxx', {"headers":xxxxfd}>这种
    public Single<List<ChapterContentBo>> getComicChapterContent(SiteSourceBean siteSourceBean, CollectionMediaBean collectionMediaBean, String chapterId, String chapterDetailUrl, String nextChapterDetailUrl) {
        return getBookChapterContent(siteSourceBean, collectionMediaBean, chapterDetailUrl, nextChapterDetailUrl).map(chapterContentBos -> {
            ChapterContentBo contentBo = chapterContentBos.get(0);
            if(contentBo == null){
                return chapterContentBos;
            }
            BookSource bookSource = GsonUtils.string2Obj(siteSourceBean.getExtraSource(), BookSource.class);
            Pair<List<String>, Map<String,String>> comicContent = new WebBook(bookSource).getComicContent(chapterDetailUrl, contentBo.getContent());
            if(comicContent.getFirst() == null || comicContent.getFirst().size() == 0){
                chapterContentBos.clear();
                return chapterContentBos;
            }

            List<ChapterContentBo> chapterContentBoList = new ArrayList<>();
            List<String> contentList = comicContent.getFirst();
            for(int i = 0; i < contentList.size(); i++){
                Map<String, String> headerMap = comicContent.getSecond();
                if(!headerMap.containsKey("Referer") && !headerMap.containsKey("referer")){
                    headerMap.put("Referer", siteSourceBean.getSiteHostUrl());
                }
                String str = contentList.get(i) ;
                ChapterContentBo chapterContentBo = new ChapterContentBo();
                chapterContentBo.setChapterUrl(chapterDetailUrl);
                chapterContentBo.setChapterId(chapterId);
                chapterContentBo.setCurPartNum(i);
                chapterContentBo.setPartTotal(contentList.size());
                chapterContentBo.setContent(str);
                chapterContentBo.setHeaders(headerMap);
                chapterContentBoList.add(chapterContentBo);

            }

            return chapterContentBoList;

        });

    }
    public Single<List<ChapterContentBo>> getBookChapterContent(SiteSourceBean siteSourceBean, CollectionMediaBean collectionMediaBean, String chapterDetailUrl, String nextChapterDetailUrl) {

        if (siteSourceBean.getSourceFrom()!= null && siteSourceBean.getSourceFrom() == SourceFromType.SOURCE_FROM_WHITEAPE) {
            return Single.create(emitter -> emitter.onError(new Throwable("非外部源")));
        }
        if (siteSourceBean.getSourceFrom()!= null && siteSourceBean.getSourceFrom() == SourceFromType.SOURCE_FROM_YUEDU2) {
            BookShelfBean bookShelfBean = convertToBookshelfBean(collectionMediaBean);
            BookSourceBean bookSourceBean = GsonUtils.string2Obj(siteSourceBean.getExtraSource(), BookSourceBean.class);
            BookChapterBean bookChapterBean = new BookChapterBean();
            bookChapterBean.setDurChapterUrl(chapterDetailUrl);
            bookChapterBean.setTag(siteSourceBean.getSiteHostUrl());
            return WebBookModel.getInstance().getBookContent(bookSourceBean, bookShelfBean, bookChapterBean, nextChapterDetailUrl).firstOrError().map(bookContentBean -> {
                List<ChapterContentBo> chapterContentBoList = new ArrayList<>();
                ChapterContentBo chapterContentBo = new ChapterContentBo();
                chapterContentBo.setContent(bookContentBean.getDurChapterContent());
                chapterContentBoList.add(chapterContentBo);
                return chapterContentBoList;

            });
        }else if(siteSourceBean.getSourceFrom() != null && siteSourceBean.getSourceFrom() == SourceFromType.SOURCE_FROM_YUEDU3) {
            Book book = convertToYuedu3Book(collectionMediaBean);
            BookSource bookSource = GsonUtils.string2Obj(siteSourceBean.getExtraSource(), BookSource.class);
            BookChapter bookChapter = new BookChapter();
            bookChapter.setUrl(chapterDetailUrl);
            bookChapter.setTag(siteSourceBean.getSiteHostUrl());
            bookChapter.setBaseUrl(collectionMediaBean.getBookSourceUrl());
            return new WebBook(bookSource).getContentSingle(book, bookChapter).map(content -> {
                List<ChapterContentBo> chapterContentBoList = new ArrayList<>();
                ChapterContentBo chapterContentBo = new ChapterContentBo();
                chapterContentBo.setContent(content);
                chapterContentBoList.add(chapterContentBo);
                return chapterContentBoList;
            });
        }
        return Single.create(emitter -> emitter.onError(new Throwable("未适配的外部源")));
    }

    private CollectionMediaBean convertYuedu3BookToCollectionMediaBean(SiteSourceBean siteSourceBean, Book book){
        CollectionMediaBean collectionMediaBean = new CollectionMediaBean();
        collectionMediaBean.setAuthor(getSafeString(book.getAuthor()));
        collectionMediaBean.setCatalogUrl(getSafeString(book.getTocUrl()));
        collectionMediaBean.setId(getSafeString(book.getBookUrl()));
        collectionMediaBean.setCoverUrl(getSafeString(book.getCoverUrl()));
        collectionMediaBean.setDesc(getSafeString(book.getIntro()));
        collectionMediaBean.setTitle(getSafeString(book.getName()));
        collectionMediaBean.setMediaType(siteSourceBean.getSiteType());
        collectionMediaBean.setVarMap(book.getVariableMap());
        collectionMediaBean.setBookSourceUrl(getSafeString(book.getOrigin()));
        return collectionMediaBean;
    }

    private CollectionMediaBean convertToCollectionMediaBean(BookShelfBean bookShelfBean) {
        CollectionMediaBean collectionMediaBean = new CollectionMediaBean();
        BookInfoBean bookInfoBean = bookShelfBean.getBookInfoBean();
        collectionMediaBean.setAuthor(getSafeString(bookInfoBean.getAuthor()));
//        collectionMediaBean.setBookSourceUrl(bookShelfBean);
//        collectionMediaBean.setCatalogNextPageUrl(bookShelfBean.getBookInfoBean().get);
        collectionMediaBean.setCatalogUrl(getSafeString(bookInfoBean.getChapterUrl()));
        collectionMediaBean.setCoverUrl(getSafeString(bookInfoBean.getCoverUrl()));
        collectionMediaBean.setDesc(getSafeString(bookInfoBean.getIntroduce()));
        collectionMediaBean.setMediaType(MediaType.MEDIA_TYPE_NOVEL);
        collectionMediaBean.setTitle(getSafeString(bookInfoBean.getName()));
        collectionMediaBean.setId(getSafeString(bookInfoBean.getNoteUrl()));
        collectionMediaBean.setVarMap((HashMap<String, String>) bookShelfBean.getVariableMap());
        return collectionMediaBean;
    }

    private Book convertToYuedu3Book(CollectionMediaBean collectionMediaBean){
        Book book = new Book();
        book.setAuthor(getSafeString(collectionMediaBean.getAuthor()));
        book.setBookUrl(getSafeString(collectionMediaBean.getId()));
        book.setTocUrl(getSafeString(collectionMediaBean.getCatalogUrl()));
        book.setCoverUrl(getSafeString(collectionMediaBean.getCoverUrl()));
        book.setCustomIntro(getSafeString(collectionMediaBean.getDesc()));
        book.setIntro(getSafeString(collectionMediaBean.getDesc()));
        book.setName(getSafeString(collectionMediaBean.getTitle()));
        book.setOrigin(getSafeString(collectionMediaBean.getBookSourceUrl()));
        return book;
    }
    private BookShelfBean convertToBookshelfBean(CollectionMediaBean collectionMediaBean) {
        BookShelfBean bookShelfBean = new BookShelfBean();
        BookInfoBean bookInfoBean = new BookInfoBean();
        bookShelfBean.setBookInfoBean(bookInfoBean);
        bookInfoBean.setAuthor(collectionMediaBean.getAuthor());
//        collectionMediaBean.setBookSourceUrl(bookShelfBean);
//        collectionMediaBean.setCatalogNextPageUrl(bookShelfBean.getBookInfoBean().get);
        bookInfoBean.setChapterUrl(collectionMediaBean.getCatalogUrl());
        bookInfoBean.setCoverUrl(collectionMediaBean.getCoverUrl());
        bookInfoBean.setIntroduce(collectionMediaBean.getDesc());
        bookInfoBean.setName(collectionMediaBean.getTitle());
        bookInfoBean.setNoteUrl(collectionMediaBean.getId());
        bookShelfBean.setNoteUrl(collectionMediaBean.getId());


        collectionMediaBean.setVarMap((HashMap<String, String>) bookShelfBean.getVariableMap());
        return bookShelfBean;
    }

    private String getSafeString(String str) {
        if (StringUtils.isEmpty(str)) {
            return "";
        }
        return str;
    }
}
